package com.sun.umbrella.service.shiro.filter.resources.impl;

import com.sun.core.enums.BaseBizEnum;
import com.sun.core.enums.BaseErrorEnum;
import com.sun.core.ex.BusinessCheckFailException;
import com.sun.umbrella.enums.ReloadStatusEnum;
import com.sun.umbrella.service.shiro.Constants;
import com.sun.umbrella.service.shiro.filter.resources.FilterChainService;
import com.sun.umbrella.dao.OauthUserMapper;
import com.sun.umbrella.domain.entity.FilterChainsDO;
import com.sun.umbrella.domain.entity.OauthUserDO;
import com.sun.umbrella.remote.RemoteService;
import com.sun.umbrella.remote.entity.FilterChains;
import com.sun.umbrella.service.shiro.session.ServerSessionDAO;
import com.sun.umbrella.session.SessionContext;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.Subject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

/**
 * @author Jay
 * @version v1.0
 * @description 业务session信息、过滤器信息
 * @date 2017-11-30 15:47
 */
@Slf4j
@Service
public class RemoteServiceImpl implements RemoteService {

    @Autowired
    private OauthUserMapper oauthUserMapper;

    @Autowired
    private FilterChainService filterChainService;

    @Autowired
    private ServerSessionDAO serverSessionDAO;

    @Autowired
    private RedisTemplate<String,List<FilterChains>> redisTemplate;

    @Autowired
    private RedisTemplate<String,Integer> redisTemplateStr;

    /**
     * 获取 web session
     *
     * @param sessionId
     * @return
     */
    @Override
    public Session getSession(Serializable sessionId) {
        return serverSessionDAO.doReadSession(sessionId);
    }


    /**
     * 创建 web session
     *
     * @param session
     * @return
     */
    @Override
    public Serializable createSession(Session session) {
        return serverSessionDAO.doCreate(session);
    }


    /**
     * 更新 web session
     *
     * @param session
     */
    @Override
    public void updateSession(Session session) {
        serverSessionDAO.doUpdate(session);
    }


    /**
     * 删除 web session
     *
     * @param session
     */
    @Override
    public void deleteSession(Session session) {
        serverSessionDAO.doDelete(session);
    }


    /**
     * 获得业务session
     *
     * @param principal
     * @return
     */
    @Override
    public SessionContext getSessionContext(String principal) {
        if (StringUtils.isEmpty(principal)) {
            throw new BusinessCheckFailException(BaseErrorEnum.BNS_CHK_ERROR.getCode(),"principal不能为空");
        }
        OauthUserDO oauthUserDO = oauthUserMapper.selectByUserName(principal);
        if (oauthUserDO == null) {
            return null;
        }
        return new SessionContext(oauthUserDO.getId(), oauthUserDO.getUserName());
    }


    /**
     * 根据sysId查询过滤器资源
     *
     * @param sysId
     * @return
     */
    @Override
    public List<FilterChains> getFilterChainsBySysId(Integer sysId) {
        if (StringUtils.isEmpty(sysId)) {
            log.error("sysId can not be null");
            throw new BusinessCheckFailException(BaseErrorEnum.BNS_CHK_ERROR.getCode(), "sysId 不能为空");
        }
        // 从redis中取
        List<FilterChains> filterChainsList = redisTemplate.opsForValue().get(Constants.getSysFilterChainKey(sysId));
        // 如果不存在则从数据库中取
        if (CollectionUtils.isEmpty(filterChainsList)) {
            // 从数据库中取
            filterChainsList = this.convert2FilterChainsList(filterChainService.getFilterChainsBySysId(sysId));
            // 如果不为空则放入redis
            if (!CollectionUtils.isEmpty(filterChainsList)){
                redisTemplate.opsForValue().set(Constants.getSysFilterChainKey(sysId), filterChainsList);
            }
        }
        return filterChainsList;
    }


    /**
     * 退出登录
     *
     * @param principal
     * @return
     */
    @Override
    public Boolean logout(String principal) {
        try {
            Subject subject = SecurityUtils.getSubject();
            if (subject.isAuthenticated()) {
                subject.logout();
            }
            return Boolean.TRUE;
        } catch (Exception e) {
            log.error(e.getMessage());
            return Boolean.FALSE;
        }
    }

    /**
     * 是否需要重新加载过滤器
     *
     * @param sysId
     * @return true: 需要重新加载  false: 不需要重新加载
     */
    @Override
    public synchronized Boolean isNeedReload(Integer sysId) {
        if (sysId == null){
            throw new BusinessCheckFailException(BaseErrorEnum.BNS_CHK_ERROR.getCode(),"sysId不能为空");
        }
        Integer status = redisTemplateStr.opsForValue().get(Constants.getSysFilterReoad(sysId));
        // 数据不存在的时候插入一条进入redis
        if (status == null){
            redisTemplateStr.opsForValue().set(Constants.getSysFilterReoad(sysId),BaseBizEnum.ZERO.getKey());
            return Boolean.TRUE;
        }
        // 需要重新加载过滤器的时候，先清除redis缓存 再 重置状态
        if (status.equals(BaseBizEnum.FIRST.getKey())){
            // 清除redis过滤器配置的缓存
            redisTemplate.opsForValue().getOperations().delete(Constants.getSysFilterChainKey(sysId));
            // 重置状态
            redisTemplateStr.opsForValue().set(Constants.getSysFilterReoad(sysId),BaseBizEnum.ZERO.getKey());
            return Boolean.TRUE;
        }
        // 不需要加载过滤器直接返回false
        if (status.equals(BaseBizEnum.ZERO.getKey())){
            return Boolean.FALSE;
        }
        // 其他的情况则重新加载过滤器
        redisTemplateStr.opsForValue().set(Constants.getSysFilterReoad(sysId),BaseBizEnum.ZERO.getKey());
        // 清除redis过滤器配置的缓存
        redisTemplate.opsForValue().getOperations().delete(Constants.getSysFilterChainKey(sysId));
        return Boolean.TRUE;
    }

    /**
     * 修改 是否需要重新加载过滤器 的状态
     *
     * @param sysId
     * @param reloadStatus 0：不需要重新加载 1：需要重新加载 {@link ReloadStatusEnum}
     * @return
     */
    @Override
    public Boolean resetReloadFilterStatus(Integer sysId, Integer reloadStatus) {
        if (sysId == null){
            throw new BusinessCheckFailException(BaseErrorEnum.BNS_CHK_ERROR.getCode(),"sysId不能为空");
        }
        if (reloadStatus == null){
            throw new BusinessCheckFailException(BaseErrorEnum.BNS_CHK_ERROR.getCode(),"reloadStatus不能为空");
        }
        if (reloadStatus != ReloadStatusEnum.NEED_RELOAD.getKey() && reloadStatus != ReloadStatusEnum.NOT_NEED_RELOAD.getKey()){
            throw new BusinessCheckFailException(BaseErrorEnum.BNS_CHK_ERROR.getCode(),"reloadStatus传入值不合法");
        }
        redisTemplateStr.opsForValue().set(Constants.getSysFilterReoad(sysId),reloadStatus);
        return Boolean.TRUE;
    }


    /*==========================================private method==========================================*/

    /**
     * List转换
     *
     * @param filterChainsDOList
     * @return
     */
    private List<FilterChains> convert2FilterChainsList(List<FilterChainsDO> filterChainsDOList) {
        if (CollectionUtils.isEmpty(filterChainsDOList)) {
            return null;
        }
        List<FilterChains> filterChains = new ArrayList<>();
        for (FilterChainsDO filterChainsDO : filterChainsDOList) {
            filterChains.add(this.convert2FilterChains(filterChainsDO));
        }
        return filterChains;
    }

    /**
     * 实体类转换
     *
     * @param filterChainsDO
     * @return
     */
    private FilterChains convert2FilterChains(FilterChainsDO filterChainsDO) {
        FilterChains filterChains = new FilterChains();
        if (filterChainsDO != null) {
            filterChains.setFilterRes(filterChainsDO.getFilterRes());
            filterChains.setMatchSign(filterChainsDO.getMatchSign());
        }
        return filterChains;
    }

}
